Elastic Beanstalk에 애플리케이션 배포, 자동화(CD)

@Wonyu· December 17, 2024 · 5 min read

이전 기록에서 ECR에 Spring Boot application 이미지 push를 자동화하는 것까지 진행했다. 이젠 이미지를 Elastic Beanstalk에 반영하는 작업을 자동화할 차례다.

먼저 Elastic Beanstalk를 실행해보자. Elastic Beanstalk는 AWS 콘솔에서 간단한 설정만으로 이전에 봤던 아래 모든 인프라 아키텍처를 구성할 수 있다. architecture

Docker 이미지를 활용할 것이기 때문에 Elastic Beanstalk 플랫폼은 Docker를 선택한다. platform 다른 환경 구성은 본인에 프로젝트에 맞게 설정한 후 환경을 생성하면, 샘플 앱이 정상적으로 실행되는 것을 확인할 수 있다. sample app

이슈

환경이 생성될 때 겪은 이슈들은 다음과 같다.

vpc 지정

Auto Scaling activity failed XX minutes ago with error: The parameter SecurityGroup is not recognized. Launching EC2 instance failed.

Auto Scaling 그룹이 인스턴스를 생성하는 과정에서 보안 그룹을 제대로 식별하지 못하는 문제가 발생했다. Stack Overflow를 참고해 VPC를 명시적으로 선택하여 문제를 바로 해결할 수 있었다. AWS의 Troubleshoot EC2 Auto Scailing 에도 위 오류가 없어서 정확한 이유는 아직 확인하지 못했다.

public ip

EC2 인스턴스를 public subnet에 생성하고, public ip를 허용해줘야 인스턴스가 Elastic Beanstalk과 연결될 수 있다.

EC2 권한

EC2가 ECR의 이미지를 pull할 수 있어야 하기 때문에 EC2의 role에 AmazonEC2ContainerRegistryReadOnly 권한을 줘야 했다.


RDS 연결

Elastic Beanstalk에서 RDS를 같은 VPC 내에 생성해주는 것은 맞지만, Spring Boot Application이 RDS와 연결되기 위해선 추가로 해줘야 하는 것들이 있다.

환경 변수

애플리케이션이 처음 실행될 때 데이터베이스 연결 정보를 설정해야 하므로 Elastic Beanstalk 환경의 구성(Configuration) 탭에서 환경 변수(Environment Variables)를 지정해야 한다. 생성된 RDS 정보를 기반으로 적절한 값을 입력한다.

jdbc:mysql://[RDS 엔드포인트]:[포트]/[데이터베이스명]

DB_URL은 위와 같은 형식으로 작성하면 되고, USERNAME과 PASSWORD는 Elastic Beanstalk 환경을 생성할 때 지정한 값을 작성하면 된다. env

rds 연결 확인

EC2 인스턴스에 SSH로 접속한 뒤, RDS에 정상적으로 연결되는지 확인한다. db connection

데이터 삽입

필요한 DB와 테이블을 생성해주고, 데이터를 추가한다. db


배포 자동화

Github Actions 스크립트 추가

이제 기존 github actions 스크립트에 docker 이미지를 실행 중인 Elastic Beanstalk에 반영하는 스크립트를 추가해보자.

name: CI/CD  
  
on:  
  push:  
    branches:  
      - master  
  
env:  
  ECR_REGISTRY: ${{ secrets.AWS_ECR_REGISTRY }}  
  ECR_REPOSITORY: ${{ secrets.AWS_PROD_ECR_REPOSITORY }}  
  IMAGE_TAG: ${{ github.sha }}-${{ github.run_number }}  
jobs:  
  build-and-push:  
    runs-on: ubuntu-latest  
  
    steps:  
      - name: Checkout repository  
        uses: actions/checkout@v3  
  
      - name: Configure AWS CLI  
        uses: aws-actions/configure-aws-credentials@v2  
        with:  
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}  
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}  
          aws-region: ap-northeast-2  
  
      - name: Log in to Amazon ECR  
        id: login-ecr  
        uses: aws-actions/amazon-ecr-login@v1  
  
      - name: Build and Push Docker Image  
        run: |  
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .  
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG  
  
      - name: Get current time  
        uses: 1466587594/get-current-time@v2  
        id: current-time  
        with:  
          format: YYYYMMDD_HH-mm-ss  
          utcOffset: "+09:00"  
  
      - name: Update Dockerrun.aws.json  
        run: |  
          sed -i "s|{{ECR_REGISTRY}}|${{ env.ECR_REGISTRY }}|g" environment/prod/Dockerrun.aws.json  
          sed -i "s|{{ECR_REPOSITORY}}|${{ env.ECR_REPOSITORY }}|g" environment/prod/Dockerrun.aws.json  
          sed -i "s|{{IMAGE_TAG}}|${{ env.IMAGE_TAG }}|g" environment/prod/Dockerrun.aws.json  
  
      - name: Beanstalk Deploy  
        uses: einaregilsson/beanstalk-deploy@v22  
        with:  
          aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }}  
          aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}  
          application_name: test-prod-app  
          environment_name: Test-prod-app-env  
          version_label: test-prod-${{steps.current-time.outputs.formattedTime}}  
          region: ap-northeast-2  
          deployment_package: environment/prod/Dockerrun.aws.json

Elastic Beanstalk 배포에 필요한 Dockerrun.aws.json 파일은 environment 디렉토리에 환경 별로 작성했다.

소스 코드 push

이제 변경된 소스 코드를 push하면, Elastic Beanstalk에 자동으로 반영된다. app 샘플 앱에서 Spring Boot 애플리케이션이 정상적으로 배포된 것을 확인할 수 있다.

소스 코드 변경

dto를 변경해보자.

// MemberDto.java
public record MemberDto(Long id, String name) {  
  
    public static MemberDto toDto(final Member member) {  
        return new MemberDto(member.getId(), member.getName());  
    }  
}

changed app 구축한 파이프라인에 의해 변경 사항이 정상적으로 반영된 것을 확인할 수 있다.

@Wonyu
Hello :) I'm Wonyu